"""
我们为什么需要单例模式？
在现实生活中：会出现很多人对一个实体进行操作的需求，
例如，数个人需要完成一辆汽车，飞机等的生产;
     数个人需要完善一个一份文档或开源项目；
再例如：干旱了，数个人只能喝一桶水（当然可以用不同的水杯）
       饥荒了，数个人需要吃一个粮仓的粮食
再例如：一个班只能有一个班长，一个国只能有一个最高领导集体他们需要综合处理
       一个班或一个国的事情，那么就得确保今天的数据要提交到班长或领导集体手里，
       而且要保证今天的班长或领导集体和明天的是同一个
再例如：（待你补充）
总之：这都是多个用户对一个实体（实例）操作的场景。如果我们想保存每一个
人对这个实体操作后产生的影响，那我们就得用单例模式。
"""
# python装饰器实现单例模式
"""
def Singleton(cls):
    _instance = {}

    def _singleton(*args, **kargs):
        if cls not in _instance:
            _instance[cls] = cls(*args, **kargs)
        return _instance[cls]

    return _singleton

@Singleton
class Leader(object):
    a = {}
    def __init__(self, x=0):
        pass
    def setAddNuber(self,key,value):
        self.a[key] = value
        print(self.a)
    def add(self):
        c = 0
        for key in self.a:
            c = self.a[key] + c
        return c

"""

"""
leader = Leader()
leader.setAddNuber(key='a',value=1)
leader.setAddNuber(key='b',value=2)
print(leader.add())
"""
"""
现在我们有一个领导，那么他要处理一个加法，那么他需要两个加数（A+B=C，A,B为加数，C为和）
于是他派出两个得力手下寻找这两个加数，他们干的很不错，一人找到一个，那么他们要确保两个人
把这两个加数交给同一个领导才能得出我们想要的C，否则......
"""

"""
import threading

# 为线程定义一个函数
def give_addNumber_to_leader(key,value):
   leader = Leader()
   leader.setAddNuber(key=key, value=value)
"""

# 创建两个线程,代表俩个得力手下
# thread_a = threading.Thread(target=give_addNumber_to_leader, args=("a", 1))
# thread_b = threading.Thread(target=give_addNumber_to_leader, args=("b", 2))
# thread_a.start()
# thread_b.start()
# leader = Leader()
# print(leader.add())
"""
thread_a和thread_b完成了任务，我们也得出了我们想要的“和”——C
注意：java单例实现方式多种多样，其中差别可参见http://www.runoob.com/design-pattern/singleton-pattern.html
"""

"""
题外话：线程的安全问题
多线程和多进程最大的不同在于，多进程中，同一个变量，各自有一份拷贝存在于每个进程中，互不影响，而多线程中，
所有变量都由所有线程共享，所以，任何一个变量都可以被任何一个线程修改，
因此，线程之间共享数据最大的危险在于多个线程同时改一个变量，把内容给改乱了。
解决这个问题就需要线程锁的概念了
"""
"""
balance = 0
def change_it_without_lock(n,thread_id):
    global balance
    # 不加锁的话 最后的值不是0
    # 线程共享数据危险在于 多个线程同时改同一个变量
    # 如果每个线程按顺序执行，那么值会是0， 但是线程时系统调度，又不确定性，交替进行
    # 没锁的话，同时修改变量
    # 所以加锁是为了同时只有一个线程再修改，别的线程表一定不能改
    for i in range(50):
        balance = balance + n
        print("子线程：" + str(thread_id)+"操作了加法得:"+str(balance))
        balance = balance - n
        print("子线程：" + str(thread_id)+"操作了减法:"+str(balance))

thread_a = threading.Thread(target=change_it_without_lock, args=(8,1) )
thread_b = threading.Thread(target=change_it_without_lock, args=(10,2) )

thread_a.start()
thread_b.start()
print('主线程：'+str(balance))
"""
"""
子线程：1操作了加法
子线程：1操作了减法
子线程：1操作了加法
子线程：1操作了减法
子线程：1操作了加法
子线程：1操作了减法
子线程：1操作了加法
子线程：2操作了加法
子线程：1操作了减法
主线程：10
子线程：1操作了加法
子线程：2操作了减法
子线程：2操作了加法
子线程：1操作了减法
子线程：2操作了减法
子线程：2操作了加法
子线程：2操作了减法
子线程：2操作了加法
子线程：2操作了减法
子线程：2操作了加法
子线程：2操作了减法
......
这体现了线程操作没有原子性，那我们想怎么样呢？
我们当然并不想等待一个线程执行完之后再执行另一个线程，那样和单线程有什么区别，
ok，我们怎么解决这个问题呢？我们需要引入线程锁的概念了，锁的引入就是为了保证线程的操作
不被别人打扰(话说正当你开心的和男/女一顿操作，也不希望被别人打扰)，专业点的说法就是
保证 操作的 “原子性”（即不可分割）----- 当然除了锁这种手段，如果你有其他手段也希望能够
分享出来。
"""

"""
lock = threading.Lock()
balance = 0
def change_it_without_lock(n,thread_id):
    global balance
    for i in range(10000):
        lock.acquire()
        balance = balance + n
        print("子线程：" + str(thread_id)+"操作了加法得:"+str(balance))
        balance = balance - n
        print("子线程：" + str(thread_id)+"操作了减法:"+str(balance))
        print("-----------------------------------------------------")
        lock.release()

thread_a = threading.Thread(target=change_it_without_lock, args=(8,1) )
thread_b = threading.Thread(target=change_it_without_lock, args=(10,2) )

thread_a.start()
thread_b.start()
"""

"""
这样我们就可以让
lock.acquire()
balance = balance + n
print("子线程：" + str(thread_id)+"操作了加法得:"+str(balance))
balance = balance - n
print("子线程：" + str(thread_id)+"操作了减法:"+str(balance))
print("-----------------------------------------------------")
lock.release()
这一段被lock包裹的代码作为一个操作原子
"""
"""
不同的语言实现锁（同步）的方式有所不同
"""

"""
死锁（1）：thread_a遇到错误无法释放资源（执行代码的权利），thread_b只能等待。
这时整个程序就无法继续进展下去，即为死锁
"""

"""
import threading
lock = threading.Lock()
balance = 0
def change_it_without_lock(n,thread_id):
    global balance
    for i in range(10):
        lock.acquire()
        balance = balance + n
        print("子线程：" + str(thread_id)+"操作了加法得:"+str(balance))
        balance = balance - n
        a = 1 / 0 # 这个错误要处理
        print("子线程：" + str(thread_id)+"操作了减法:"+str(balance))
        print("-----------------------------------------------------")
        lock.release()

thread_a = threading.Thread(target=change_it_without_lock, args=(8,1) )
thread_b = threading.Thread(target=change_it_without_lock, args=(10,2) )

thread_a.start()
thread_b.start()
"""

"""
死锁（2）：当有两个锁时，也很容易造成死锁
"""
"""
import threading

_base_lock = threading.Lock()
_pos_lock  = threading.Lock()
_base = 1

def _sum(x, y):
    # Time 1
    with _base_lock:
        # Time 3
        for i in range(100):
            with _pos_lock:
                print('I am _pos_lock')
def _minus(x, y):
    # Time 0
    with _pos_lock:
        # Time 2
        for i in range(100):
            with _base_lock:
                print('I am _base_lock')

thread_a = threading.Thread(target=_sum, args=(8,1) )
thread_b = threading.Thread(target=_minus, args=(10,2) )

thread_a.start()
thread_b.start()
"""
"""
上面的代码会让thread_a拿不到_base_lock，而thread_b拿不到_pos_lock，从而造成死锁，那么如何解决这个问题呢？
就要涉及到线程的优先级了
"""

"""
线程优先级队列
"""
"""
import queue
import threading
import time

exitFlag = 0

class myThread (threading.Thread):
    def __init__(self, threadID, name, q):
        threading.Thread.__init__(self)
        self.threadID = threadID
        self.name = name
        self.q = q
    def run(self):
        print ("开启线程：" + self.name)
        process_data(self.name, self.q)
        print ("退出线程：" + self.name)

def process_data(threadName, q):
    while not exitFlag:
        queueLock.acquire()
        if not workQueue.empty():
            data = q.get()
            queueLock.release()
            print ("%s processing %s" % (threadName, data))
        else:
            queueLock.release()
        time.sleep(1)

threadList = ["Thread-1", "Thread-2", "Thread-3"]
nameList = ["One", "Two", "Three", "Four", "Five"]
queueLock = threading.Lock()
workQueue = queue.Queue(10)
threads = []
threadID = 1

# 创建新线程
for tName in threadList:
    thread = myThread(threadID, tName, workQueue)
    thread.start()
    threads.append(thread)
    threadID += 1

# 填充队列
queueLock.acquire()
for word in nameList:
    workQueue.put(word)
queueLock.release()

# 等待队列清空
while not workQueue.empty():
    pass

# 通知线程是时候退出
exitFlag = 1

# 等待所有线程完成
for t in threads:
    t.join()
print ("退出主线程")
"""
"""
import time
import threading

_base_lock = threading.Lock()
_pos_lock  = threading.Lock()
_base = 1

def _sum(thread_id):
    # Time 1
    with _base_lock:
        # Time 3
        print(threading.current_thread())
        for i in range(800):
            with _pos_lock:
                print('I am thread:'+thread_id+str(i))
                time.sleep(2)

def _minus(thread_id):
    # Time 0
    _pos_lock.acquire()
    with _pos_lock:
        # Time 2
        for i in range(1000):
            with _base_lock:
                print('I am thread:'+thread_id+str(i))

thread_a = threading.Thread(target=_sum, args=('a', ))
thread_b = threading.Thread(target=_minus, args=('b', ))
thread_a.start()
thread_b.start()
print(threading.current_thread())
"""

"""
悲观锁(Pessimistic Lock), 顾名思义，就是很悲观，
每次去拿数据的时候都认为别人会修改，所以每次在拿数据的时候都会上锁，这样别人想拿这个数据就会block直到它拿到锁。
传统的关系型数据库里边就用到了很多这种锁机制，比如行锁，表锁等，读锁，写锁等，都是在做操作之前先上锁。

乐观锁(Optimistic Lock), 顾名思义，就是很乐观，每次去拿数据的时候都认为别人不会修改，
所以不会上锁，但是在更新的时候会判断一下在此期间别人有没有去更新这个数据，可以使用版本号等机制。
乐观锁适用于多读的应用类型，这样可以提高吞吐量。
悲观锁比较常用。

互斥锁
互斥锁是一个二元变量，其状态为开锁(允许0)和上锁(禁止1)，将某个共享资源与某个特定互斥锁在逻辑上绑定(要申请该资源必须先获取锁)。
(1)访问公共资源前，必须申请该互斥锁，若处于开锁状态，则申请到锁对象，并立即占有该锁，以防止其他线程访问该资源；如果该互斥锁处于锁定状态，则阻塞当前线程。
(2)只有锁定该互斥锁的进程才能释放该互斥锁，其他线程试图释放无效。
"""

import time
import threading

_base_lock = threading.Lock()
_pos_lock  = threading.Lock()
_base = 1

def _sum(thread_id):
    # Time 1
    with _base_lock:
        # Time 3
        print(threading.current_thread())
        for i in range(80):
            _pos_lock.acquire(timeout=3)
            print('I am thread:'+thread_id+str(i))
            # time.sleep()
            _pos_lock.release()

def _minus(thread_id):
    # Time 0
    _pos_lock.acquire()
    with _pos_lock:
        # Time 2
        for i in range(100):
            _base_lock.acquire(timeout=2)
            print('I am thread:'+thread_id+str(i))
            _base_lock.release()

thread_a = threading.Thread(target=_sum, args=('a', ))
thread_b = threading.Thread(target=_minus, args=('b', ))
thread_a.start()
thread_b.start()
"""
多线程加锁的原则：
0. 多线程、进程并行访问共享资源时，一定要加锁保护
1. 每个锁只锁一个唯一共享资源，确保加锁的范围尽 量小，只锁对应资源操作代码 
2. 避免嵌套加锁；如果必须加锁，务必保证不同地方的加锁顺序是一样的
3. 锁中避免使用跳转语句
4. 锁中避免调用函数；如果必须调用函数，务必保证不会造成死锁
5. 可重入函数尽量只使用局部变量和函数参数，少用全局变量、静态变量
6. 进程间通讯，使用自己保证互斥的数据库系统、共享内存，或socket消息机制；
   尽量避免使 用文件等进程无法管理的资源
   
7. 发挥你的想象设计一个更好的锁

总之一句话要使用锁就得考虑避免死锁
"""


"""
补充：
关于单例的懒汉和饿汉模式，需要的时候加载叫懒加载，随类初始化称之为饿汉。
懒加载，和饿加载的概念不仅应用于单例模式，也应用于整个系统的设计当中
"""